
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "AntDefine.h"
#include "define.h"
#include "Debug.h"
#include "Helper.h"
#include "configParser.h"
 
using namespace std;
Helper * Helper::_instance = NULL;

Helper::Helper() 
{
	m_pConnMs = NULL;
        m_sConfFile = "";
        m_sIPAcl = "";
        m_iPort = 0;
        m_iwPort = 0;
        m_iThreads = 10;
        m_iDefaultTimer = 0;
}

Helper::~Helper()
{
	if( m_pConnMs != NULL)
	{
		delete m_pConnMs;
		m_pConnMs = NULL;
	}
}

Helper * Helper::Instance(const char *confFile)
{
	if (NULL == _instance) 
	{
		_instance = new Helper();
		if ( !_instance->Startup(confFile) ) {
			delete _instance; 
			_instance = NULL;
		}
	}
	return _instance;
}

void Helper::Release(bool bForce)
{
	if (_instance) 
	{
		if(bForce)
		{
			delete _instance; 
			_instance = NULL;
		}
	}
}

bool  Helper::Startup(const char *confFile)
{
	if(confFile != NULL)
		m_sConfFile  = confFile;
	if(m_sConfFile.length() < 1)
	{
		ERRO<<": no helper.conf be given!"<<endl;
		return false;
	}
	struct stat st;
	if (lstat(confFile, &st) != 0)
	{
		ERRO<<":"<<confFile<<" is not exist!"<<endl;
		return false;
	}

	configParser cfg;
	map <string, string > g_Confs;
	g_Confs = cfg.Parser(confFile);
	if(g_Confs.size() <= 0)
	{
		cerr<<"error to explain conf :"<< confFile <<endl;
		return false;
	}
       
        map <string , string >::iterator iter;
        for(iter=g_Confs.begin(); iter != g_Confs.end(); iter++)
        {
             cout<< iter->first.c_str()<<"="<<iter->second.c_str()<<endl;
        }

	if( g_Confs.find("ip_acl_config") != g_Confs.end())
		m_sIPAcl  = g_Confs["ip_acl_config"];

	if( g_Confs.find("connection_config") != g_Confs.end())
		m_sConn_Cfg  = g_Confs["connection_config"];
        else
        {
              ERRO<<"no connection_config to set!"<<endl;
              return false; 
        }
	if( g_Confs.find("port") != g_Confs.end())
	{
		string sport  = g_Confs["port"];
		m_iPort = atoi(sport.c_str());
	}
	else
		m_iPort = 4080;
	INFO<<"listen port = "<< m_iPort <<endl;

	if( g_Confs.find("wport") != g_Confs.end())
	{
		string sport  = g_Confs["wport"];
		m_iwPort = atoi(sport.c_str());
	}
	else
		m_iwPort = 0;

	if( g_Confs.find("threads") != g_Confs.end())
	{
		string sport  = g_Confs["threads"];
		m_iThreads = atoi(sport.c_str());
	}
	else
		m_iThreads = 10;

        int itype = 0;
	if( g_Confs.find("connection_type") != g_Confs.end())
	{
		string stype  = g_Confs["connection_type"];
                itype = atoi(stype.c_str());
	}

        if(itype<0 || itype > 3)
        {
             ERRO<<"ERROR: connection_type = "<< itype <<endl;
             return false;
	}  

	int maxReq = 999;
	if( g_Confs.find("max_request") != g_Confs.end())
	{
		string max  = g_Confs["max_request"];
		maxReq = atoi(max.c_str());
                if(maxReq < 2)
                   maxReq = 10;
	}

        INFO <<" max_request = "<< maxReq << endl;

	if( g_Confs.find("defaultTimer") != g_Confs.end())
	{
		string st  = g_Confs["defaultTimer"];
		m_iDefaultTimer = atoi(st.c_str());
		if(m_iDefaultTimer < 0)
			m_iDefaultTimer = 0;
	}

	m_izWindow = m_ipWindow = 0; 
	if( g_Confs.find("auto_restart") != g_Confs.end())
	{
		string st  = g_Confs["auto_restart"];
		size_t idx = st.find('/', 0);
		if(idx>0 && idx < st.length())
		{
			string sw = st.substr(0, idx);
			string bw = st.substr(idx + 1, st.length()- idx -1);
                        m_izWindow = atoi(sw.c_str());
                        m_ipWindow = atoi(bw.c_str());
                        if(m_ipWindow < m_izWindow)
                             m_ipWindow = 0;
                        DEBUG4("zWindow = ",m_izWindow, "; pWindows = ", m_ipWindow);
		}
	}

	if(m_sConn_Cfg.length() > 0)
	{ 
		m_pConnMs = new ConnManager(itype);
		bool bret = m_pConnMs->Initialize(m_sConn_Cfg.c_str());
		if(!bret)
		{
			ERRO <<"return = false: can't Initialize Connection Manager Instance"<<endl;
			delete m_pConnMs;
			m_pConnMs = NULL;
			return false;
		}
		m_pConnMs->SetMaxRequest(maxReq);
               // m_pConnMs->SetAutoRestart( bRestart);
	}
        else
        {
            INFO<<"No CACHE SYSTEM to USE !!!! "<<endl;
            return false;
        }

	return true;
}

int Helper :: Timer(long time)
{
	if(m_ipWindow > 0 && m_izWindow < m_ipWindow)
	{
             int sw = (time/m_iDefaultTimer);
             if(m_izWindow == sw%m_ipWindow)
             {
                  INFO <<"it's time to restart now = "<< time  <<endl; 
                  exit(-1); 
             }
	}
        return 0;
}

int Helper ::  Process(char *data, unsigned dlen,char **wbuf, unsigned &wsize, unsigned &wbytes, const char *ip)
{
        DEBUG4("data=", data, "; length=", dlen);
	unsigned int uint_len = sizeof(unsigned);
	int iret = ANT_CONN_ERR_CLOSE ;
	unsigned minlen = uint_len + 2;
	bool bReply = false;
	if( dlen > minlen && (*data)=='P')   
	{
		unsigned hash_key;
		char *pChar = (char*)data + 1;
		bcopy(pChar, &hash_key, uint_len);
		char rtype = *(data + uint_len + 1);
		if((int) rtype == 1)
			bReply = true;
		DEBUG4("rtype = ", (int)rtype, "; hash_key=",hash_key);
		if(m_pConnMs != NULL)
			iret = m_pConnMs->Proxy(hash_key, data + minlen , dlen - minlen, wbuf, wsize, wbytes, bReply);
		if(bReply && wbytes <= 0)
			 build_return_message(wbuf, -1, wsize, wbytes);
                 if(!bReply)  
                        return ANT_CONN_NO_REPLY;
	}

        if(iret < 0 )
                return ANT_CONN_ERR_CLOSE;
        return iret;
}

int Helper :: build_return_message(char **wbuf, int retcode,unsigned &wsize, unsigned &wbytes)
{
        wbytes = 2*sizeof(int);
        DEBUG4("wsize=", wsize, "; wbytes=", wbytes );
        if(wsize < wbytes)
        {
DEBUG1("realloc(*wbuf, wbytes+1)");
                *wbuf = (char *)realloc(*wbuf, wbytes +1);
                if(*wbuf == NULL){
                        ERRO<<"can't malloc for write buffer !"<<endl;
                        wsize = wbytes = 0;
                        return  ANT_CONN_ERR_CLOSE;
                }
                wsize = wbytes + 1;
        }

        unsigned usedlen = 0;
//DEBUG4("wbuf =", *wbuf,"; bcopy wbytes = ", wbytes);
        bcopy(&wbytes, *wbuf + usedlen, sizeof(unsigned int));            //str_length
        usedlen += sizeof(unsigned int);
DEBUG2("bcopy retcode = ", retcode);
        bcopy(&retcode, *wbuf + usedlen, sizeof(int));   // retcode
        return 0;
}

